/**
 * simple view object
 * created by David hua
 * @2016/7/31
 */
var vo = function(params){
	if(params.el == null || params.el == '' ){
		throw new Error("the parameter of 'el' is empty");
	}
	var $el = params.el;
	if($el.substring(0,1) == '%'){//'%name'
		this.el = getElementsByAttr_r(document,'name',$el.substring(1));
	}else if($el.substring(0,1) == '#'){
		this.el = document.getElementById($el.substring(1));
	}else{
		throw new Error('illegal el');
	}
	if(params.datas != null){
		this.setDatas(params.datas);
		this.flush();
	}
	if(params.methods != null&& params.methods instanceof Object){
		this.setMethods(params.methods);
	}
};
vo.prototype = {
	mapper : function(key,value){//map the K/V to view(elements)
		if(this.el instanceof Array){
			for(var i = 0;i<this.el.length;i++){
				var props = getElementsByAttr_r(this.el[i],'name',key);
				if(null != props){
					for(var j = 0;j<props.length;j++){
						this.setValue(props[j],value);
					}
				}
			}
		}else{
			var props = getElementsByAttr_r(this.el,'name',key);
			if(null != props){
				for(var j = 0;j<props.length;j++){
					this.setValue(props[j],value);
				}
			}
		}
	},
	bindMethod : function(el){//bind the methods to view(elements)
		//scan the elements which have attribute named 'vo-m'
		var elements = getElementsByAttr_r(el,'vo-m', '[\\w\\W]{1,}');
		var eventRegExp = new RegExp('[\\w\\W]{1,}(?=[:])');
		for(var i = 0;i<elements.length;i++){
			var attr = elements[i].getAttribute('vo-m');//event:method
			var event = eventRegExp.exec(attr);//event like 'click','blur'....
			var method = attr.substring(event[0].length+1);
			var wrapper = function(){//wrap the method to add plug-in method
				this.invoke(method);
				this.methods[method]();
			}.bind(this);
			if(event == 'click'){
				elements[i].onclick = wrapper;
			}else if(event == 'change'){
				elements[i].onchange = wrapper;
			}else if(event == 'blur'){
				elements[i].onblur = wrapper;
			}
		}
	},
	setValue : function(element,value){//set value according to tagName,such as 'input','div'..
		if(element.tagName.toLowerCase() == 'input'){
			element.value = value;
		}else{
			element.innerHTML = value;
		}
	},
	flush : function(){
		for(var i in this.datas){
			this.mapper(i,this.datas[i]);
		}
	},
	modify : function(key,value){//modify the K/V
		this.invoke('modify');
		this.datas[key] = value;
		this.mapper(key,value);
	},
	del : function(key){//delete K/V (doesn't affect the HTML code)
		this.invoke('del');
		delete this.datas[key];
		this.mapper(key,'');
	},
	get : function(key){//get V from K
		this.invoke('get');
		return this.datas[key];
	},
	add : function(key,value){// add K/V
		invoke('add');
		this.datas[key] = value;
		this.mapper(key,value);
	},
	setDatas : function(datas_new){
		this.datas = datas_new;
	},
	getDatas : function(){
		return this.datas;
	},
	setMethods : function(methods){
		if(typeof(this.methods) == 'undefined'){
			this.methods = {};
		}
		for(var method in  methods){
			this.methods[method] = methods[method].bind(this,this.datas);
		}
		if(this.el instanceof Array){
			for(var i = 0;i<this.el.length;i++){
				this.bindMethod(this.el[i]);
			}
		}else{
			this.bindMethod(this.el);
		}
	},
	invoke : function(method){//invoke the plug-in methods for model
		if(typeof(VO)!='undefined'){
			var index = null;
			if(this.el instanceof Array){
				index = this.el.name;
			}else{
				index = this.el.id;
			}
			VO.invoke(index,method);
		}
	}
};
function getElementsByAttr_r(root,attr,regExp){
	var reg = new RegExp(regExp);
	var elements = root.getElementsByTagName('*');
	var returns = [];
	if(elements != null && elements.length > 0){
		for(var i = 0;i<elements.length;i++){
			var attribute = elements[i].getAttribute(attr);
			if(attribute!=null&&reg.test(attribute)){
				returns.push(elements[i]);
			}
		}
	}
	return returns;
}